package com.internetCafes.spms.web.customer.bizservice.impl.role;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.internetCafes.spms.common.utils.ShiroUtils;
import com.internetCafes.spms.common.utils.entity.page.PageItemDTO;
import com.internetCafes.spms.common.utils.entity.page.PageItemUtil;
import com.internetCafes.spms.common.utils.entity.page.PageItemVO;
import com.internetCafes.spms.core.exception.RRException;
import com.internetCafes.spms.core.exception.RRExceptionCodeEnum;
import com.internetCafes.spms.web.customer.bizservice.role.IEntRoleInfoBizService;
import com.internetCafes.spms.web.customer.common.cont.CommonConst;
import com.internetCafes.spms.web.customer.common.cont.RoleConst;
import com.internetCafes.spms.web.customer.common.util.UserInfoUtil;
import com.internetCafes.spms.web.customer.entity.role.EntRoleInfo;
import com.internetCafes.spms.web.customer.entity.role.RoleDeptDataAsso;
import com.internetCafes.spms.web.customer.model.role.*;
import com.internetCafes.spms.web.customer.service.role.IEntRoleInfoService;
import com.internetCafes.spms.web.customer.service.role.IRoleDeptDataAssoService;
import com.internetCafes.spms.web.sys.model.UserInfo;
import com.internetCafes.spms.web.sys.model.UserRoleInfo;
import com.internetCafes.spms.web.sys.service.RoleMenuInfoService;
import com.internetCafes.spms.web.sys.service.UserRoleInfoService;
import com.sms.common.util.hutool.core.bean.BeanUtil;
import com.sms.common.util.hutool.core.collection.CollectionUtil;
import com.sms.common.util.hutool.core.util.ObjectUtil;
import com.sms.common.util.hutool.core.util.StrUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class EntRoleInfoBizServiceImpl implements IEntRoleInfoBizService {

    @Autowired
    private RoleMenuInfoService roleMenuInfoService;
    @Autowired
    private IEntRoleInfoService roleInfoService;
    @Autowired
    private IRoleDeptDataAssoService roleDeptAssoService;
    @Autowired
    private UserRoleInfoService userRoleService;

    @Override
    public List<RoleMenuListRes> listRoleMenu() {
        List<RoleMenuPo> userMenuList = roleInfoService.getUserMenuList(ShiroUtils.getUserId());

        Map<Long, List<RoleMenuPo>> menuListGroupByParentId = userMenuList.stream()
                .collect(Collectors.groupingBy(RoleMenuPo::getParentId));

        RoleMenuListRes tempMenu = new RoleMenuListRes();

        List<RoleMenuListRes.MenuAction> menuActionList = userMenuList.stream()
                .map(po -> tempMenu.new MenuAction(po.getMenuId(), po.getName(), po.getParentId(), po.getOrderNum()))
                .sorted(Comparator.comparing(RoleMenuListRes.MenuAction::getOrderNum))
                .collect(Collectors.toList());

        List<RoleMenuPo> rootList = userMenuList.stream()
                .filter(po -> RoleConst.MENU_ROOT_PARENT_ID.equals(po.getParentId()))
                .sorted(Comparator.comparing(RoleMenuPo::getOrderNum))
                .collect(Collectors.toList());

        List<RoleMenuListRes> resultList = new ArrayList<>();

        RoleMenuListRes result;
        for (RoleMenuPo rootPo : rootList) {

            if (!menuListGroupByParentId.containsKey(rootPo.getMenuId())) {
                result = new RoleMenuListRes();
                result.setCatalogId(rootPo.getParentId())
                        .setCatalogName(rootPo.getName());
                resultList.add(result);
                continue;
            }
            List<RoleMenuPo> roleMenuPoList = menuListGroupByParentId.get(rootPo.getMenuId());
            roleMenuPoList.sort(Comparator.comparing(RoleMenuPo::getOrderNum));
            for (RoleMenuPo roleMenuPo : roleMenuPoList) {
                result = new RoleMenuListRes();
                result.setCatalogId(rootPo.getMenuId())
                        .setCatalogName(rootPo.getName())
                        .setId(roleMenuPo.getMenuId())
                        .setName(roleMenuPo.getName())
                        .setOrderNum(roleMenuPo.getOrderNum());
                result.setActionList(
                        listActionByParentId(
                                result.new MenuAction(roleMenuPo.getMenuId(), roleMenuPo.getName(), roleMenuPo.getParentId(), roleMenuPo.getOrderNum()),
                                menuActionList));
                resultList.add(result);
            }
        }

        return resultList;
    }

    private List<RoleMenuListRes.MenuAction> listActionByParentId(RoleMenuListRes.MenuAction menu, List<RoleMenuListRes.MenuAction> actionList) {
        return actionList.stream()
                .filter(action -> menu.getId().equals(action.getParentId()))
                .peek(action -> action.setChildren(listActionByParentId(action, actionList)))
                .collect(Collectors.toList());
    }

    @Override
    public List<RoleMenuTreeRes> getMenuTree() {
        List<RoleMenuPo> userMenuList = roleInfoService.getUserMenuList(ShiroUtils.getUserId());
        List<RoleMenuTreeRes> res = userMenuList.stream()
                .filter(po -> RoleConst.MENU_ROOT_PARENT_ID.equals(po.getParentId()))
                .map(po ->
                        new RoleMenuTreeRes().setId(po.getMenuId())
                                .setName(po.getName())
                                .setParentId(po.getParentId())
                                .setOrderNum(po.getOrderNum()))
                .sorted(Comparator.comparing(RoleMenuTreeRes::getOrderNum))
                .collect(Collectors.toList());
        roleMenuEncap(res, userMenuList);
        return res;
    }

    private void roleMenuEncap(List<RoleMenuTreeRes> rootList, List<RoleMenuPo> userMenuList) {

        List<Long> rootIdList = rootList.stream()
                .map(RoleMenuTreeRes::getId)
                .collect(Collectors.toList());

        List<RoleMenuTreeRes> childrenList = userMenuList.stream()
                .filter(po -> rootIdList.contains(po.getParentId()))
                .map(po -> new RoleMenuTreeRes().setId(po.getMenuId())
                        .setName(po.getName())
                        .setParentId(po.getParentId())
                        .setOrderNum(po.getOrderNum()))
                .collect(Collectors.toList());

        if (CollectionUtil.isNotEmpty(childrenList)) {
            roleMenuEncap(childrenList, userMenuList);
            Map<Long, List<RoleMenuTreeRes>> childrenGroupByParentId = childrenList.stream()
                    .collect(Collectors.groupingBy(RoleMenuTreeRes::getParentId));

            for (RoleMenuTreeRes root : rootList) {
                if (childrenGroupByParentId.containsKey(root.getId())) {
                    childrenGroupByParentId.get(root.getId())
                            .sort(Comparator.comparing(RoleMenuTreeRes::getOrderNum));
                    root.setChildren(childrenGroupByParentId.get(root.getId()));
                }
            }
        }
    }

    @Override
    public List<EntRoleListRes> roleList() {

        EntRoleListDo roleInfoDo = new EntRoleListDo();
        roleInfoDo.setTenantId(ShiroUtils.getUserEntity().getDeptId());
        UserInfo userEntity = ShiroUtils.getUserEntity();

        if (!UserInfoUtil.entSuperAdminJudge(userEntity)) {
            List<Long> idList = new ArrayList<>();
            idList.add(-1L);
            List<Long> roleIdList = roleInfoService.getBranchIdListByUserId(userEntity.getId());
            if (CollectionUtil.isNotEmpty(roleIdList)) {
                idList.addAll(roleIdList);
            }
            roleInfoDo.setIdList(idList);
        }

        return roleInfoService.poList(roleInfoDo)
                .stream()
                .map(po -> {
                    EntRoleListRes res = new EntRoleListRes();
                    BeanUtils.copyProperties(po, res);
                    return res;
                }).collect(Collectors.toList());
    }

    @Override
    public EntRoleDetailRes getRoleInfoDetail(Long id) {

        EntRoleInfo roleInfo = roleInfoService.getById(id);

        if (ObjectUtil.isNull(roleInfo)) {
            throw new RRException(RRExceptionCodeEnum.DATA_NOT_EXIST);
        }

        EntRoleDetailPo detailPo = roleInfoService.getDetailById(id);

        List<Long> menuIdList = roleMenuInfoService.queryMenuIdList(id);

        List<Long> deptIdList = roleDeptAssoService.list(
                        new LambdaQueryWrapper<RoleDeptDataAsso>()
                                .eq(RoleDeptDataAsso::getRoleId, id))
                .stream()
                .map(RoleDeptDataAsso::getDeptId)
                .collect(Collectors.toList());

        EntRoleDetailRes detailRes = new EntRoleDetailRes();
        BeanUtils.copyProperties(detailPo, detailRes);
        detailRes.setMenuIdList(menuIdList).setDeptIdList(deptIdList);

        UserInfo userEntity = ShiroUtils.getUserEntity();
        if (!UserInfoUtil.entSuperAdminJudge(userEntity)) {
            // 判断是否有权限修改角色信息
            List<Long> roleIdList = roleInfoService.getBranchIdListByUserId(userEntity.getId());
            List<Long> currentUserRoleIdList = userRoleService.queryRoleIdList(userEntity.getUserId());
            if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(roleIdList) && roleIdList.contains(id)
                    && cn.hutool.core.collection.CollectionUtil.isNotEmpty(currentUserRoleIdList) && !currentUserRoleIdList.contains(id)) {
                detailRes.setUpdateAble(true);
            }
        } else {
            detailRes.setUpdateAble(true);
        }
        return detailRes;
    }

    @Override
    public List<EntRoleTreeRes> getRoleTreeList() {

        List<EntRoleInfo> entRoleList = roleInfoService.list(new LambdaQueryWrapper<EntRoleInfo>().eq(EntRoleInfo::getTenantId, ShiroUtils.getUserEntity().getDeptId()));

        // 获取第一级角色
        List<EntRoleTreeRes> roleTreeResList = entRoleList.stream()
                .filter(roleInfo -> ObjectUtil.isNull(roleInfo.getParentId())
                        || RoleConst.ROLE_ROOT_PARENT_ID.equals(roleInfo.getParentId()))
                .map(roleInfo -> new EntRoleTreeRes(roleInfo.getId(), roleInfo.getName(), roleInfo.getParentId()))
                .collect(Collectors.toList());

        // 递归封装所有角色结构
        roleTreeListRecursion(roleTreeResList, entRoleList);

        return roleTreeResList;
    }

    private void roleTreeListRecursion(List<EntRoleTreeRes> roleTreeResList, List<EntRoleInfo> entRoleList) {
        if (CollectionUtil.isEmpty(roleTreeResList)) {
            return;
        }

        List<Long> roleIdList = roleTreeResList.stream()
                .map(EntRoleTreeRes::getId)
                .collect(Collectors.toList());

        List<EntRoleTreeRes> children = entRoleList.stream()
                .filter(roleInfo -> roleIdList.contains(roleInfo.getParentId()))
                .map(roleInfo -> new EntRoleTreeRes(roleInfo.getId(), roleInfo.getName(), roleInfo.getParentId()))
                .collect(Collectors.toList());

        roleTreeListRecursion(children, entRoleList);

        Map<Long, List<EntRoleTreeRes>> childrenGroupByParentId
                = children.stream().collect(Collectors.groupingBy(EntRoleTreeRes::getParentId));

        for (EntRoleTreeRes treeRes : roleTreeResList) {
            if (childrenGroupByParentId.containsKey(treeRes.getId())) {
                treeRes.setChildren(childrenGroupByParentId.get(treeRes.getId()));
            }
        }
    }

    @Override
    public PageItemVO<EntRolePagingRes> roleInfoPaging(PageItemDTO<EntRolePagingReq> dto) {
        Page<EntRoleInfo> pageDo = new Page<>(dto.getPageNum(), dto.getPageSize());
        EntRoleInfoDo roleInfoDo = new EntRoleInfoDo();
        UserInfo userEntity = ShiroUtils.getUserEntity();

        roleInfoDo.setTenantId(userEntity.getTenantId());
        roleInfoDo.setTypeId(RoleConst.RoleType.ENT.getId());
        EntRolePagingReq conditions = dto.getConditions();
        if (ObjectUtil.isNotNull(conditions)) {
            if (StrUtil.isNotBlank(conditions.getName())) {
                roleInfoDo.setName(conditions.getName());
            }
            if (StrUtil.isNotBlank(conditions.getRoleCode())) {
                roleInfoDo.setRoleCode(conditions.getRoleCode());
            }
        }
        if (!UserInfoUtil.entSuperAdminJudge(userEntity)) {
            List<Long> idList = new ArrayList<>();
            idList.add(-1L);
            List<Long> roleIdList = roleInfoService.getBranchIdListByUserId(userEntity.getId());
            if (CollectionUtil.isNotEmpty(roleIdList)) {
                idList.addAll(roleIdList);
            }
            roleInfoDo.setIdList(idList);
        }

        Page<EntRoleInfoPo> pagingPo = roleInfoService.pagingPo(pageDo, roleInfoDo);
        PageItemVO<EntRolePagingRes> pagingVo = PageItemUtil.toPageItemVO(pagingPo, new PageItemVO<>());
        pagingVo.setRecords(
                pagingPo.getRecords()
                        .stream()
                        .map(po -> cn.hutool.core.bean.BeanUtil.toBean(po, EntRolePagingRes.class))
                        .collect(Collectors.toList()));
        return pagingVo;
    }

    @Override
    @Transactional
    public boolean updateRoleInfo(EntRoleUpdateReq req) {
        // 参数判断
        if (ObjectUtil.isNull(req.getId()) ||
                StrUtil.isBlank(req.getName()) || ObjectUtil.isNull(req.getManagerFlag())
                || !CommonConst.FlagEnum.getFlagList().contains(req.getManagerFlag())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }

        // 角色信息存在判断
        EntRoleInfo roleInfo = roleInfoService.getById(req.getId());
        if (ObjectUtil.isNull(roleInfo)) {
            throw new RRException(RRExceptionCodeEnum.DATA_NOT_EXIST);
        }

        UserInfo userEntity = ShiroUtils.getUserEntity();
        // 角色名称重复判断
        LambdaQueryWrapper<EntRoleInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EntRoleInfo::getName, req.getName())
                .eq(EntRoleInfo::getTenantId, userEntity.getTenantId())
                .ne(EntRoleInfo::getId, req.getId());
        List<EntRoleInfo> existRoleInfoList = roleInfoService.list(queryWrapper);
        if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(existRoleInfoList)) {
            throw new RRException(RRExceptionCodeEnum.ROLE_NAME_REPEAT);
        }

        // 当前用户的角色的修改权限 如果是超级管理员|不需要验证
        List<Long> menuIdList = req.getMenuIdList();
        List<Long> deptIdList = req.getDeptIdList();
        if (!UserInfoUtil.entSuperAdminJudge(userEntity)) {
            List<Long> currentUserRoleIdList = userRoleService.queryRoleIdList(userEntity.getId());
            if (currentUserRoleIdList.contains(req.getId())) {
                throw new RRException(RRExceptionCodeEnum.ROLE_UPDATE_FAIL_CAN_NOT_UPDATE_OWN_ROLE);
            }
            // 判断是否有权限对目前角色进行操作
            List<Long> roleIdList = roleInfoService.getBranchIdListByUserId(userEntity.getId());
            if (!roleIdList.contains(req.getId())) {
                throw new RRException(RRExceptionCodeEnum.ROLE_UPDATE_FAIL_CAN_NOT_UPDATE_NOT_LOWER_LEVEL_ROLE);
            }

            // 菜单信息过滤
            List<Long> currentUserMenuIdList = roleInfoService.getUserMenuList(userEntity.getId())
                    .stream().map(RoleMenuPo::getMenuId)
                    .collect(Collectors.toList());
            currentUserMenuIdList = cn.hutool.core.collection.CollectionUtil.isNotEmpty(currentUserMenuIdList) ? currentUserMenuIdList : new ArrayList<>();
            if (CollectionUtil.isNotEmpty(menuIdList) && !currentUserMenuIdList.containsAll(menuIdList)) {
                throw new RRException(RRExceptionCodeEnum.ROLE_UPDATE_FAIL_CAN_NOT_UPDATE_NO_MENU_JURIS);
            }

            // 部门数据过滤 - 获取当前用户角色数据配置
            List<Long> currentUserDeptIdList = roleDeptAssoService.listPo(new RoleDeptDataDo().setUserId(userEntity.getId()))
                    .stream().map(RoleDeptDataPo::getDeptId)
                    .collect(Collectors.toList());

            // 部门数据过滤 - 部门数据权限覆盖配置
            if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(deptIdList) && cn.hutool.core.collection.CollectionUtil.isNotEmpty(currentUserDeptIdList)) {
                deptIdList = deptIdList.stream()
                        .filter(currentUserDeptIdList::contains)
                        .collect(Collectors.toList());
            }
            if (CollectionUtil.isNotEmpty(currentUserDeptIdList)) {
                deptIdList = new ArrayList<>();
            }
        }

        EntRoleInfo entRoleInfoDo = new EntRoleInfo();
        BeanUtil.copyProperties(req, entRoleInfoDo);

        // 上级角色id赋予默认值
        if (ObjectUtil.isNull(req.getParentId())) {
            entRoleInfoDo.setParentId(RoleConst.ROLE_ROOT_PARENT_ID);
        }
        boolean saveResult = roleInfoService.updateById(entRoleInfoDo);
        if (!saveResult) {
            return false;
        }

        // 覆盖保存保存角色与菜单关系
        roleMenuInfoService.saveOrUpdate(req.getId(), menuIdList);

        // 保存角色与部门id关系
        roleDeptAssoService.updateRoleDeptList(req.getId(), deptIdList);

        return true;
    }

    @Override
    @Transactional
    public boolean deleteRoleByIdArray(Long[] roleIdArray) {
        if (ObjectUtil.isNull(roleIdArray)) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }
        List<UserRoleInfo> userRoleAssoList = userRoleService.queryListByRoleIdList(roleIdArray);
        if (CollectionUtil.isNotEmpty(userRoleAssoList)) {
            throw new RRException(RRExceptionCodeEnum.ROLE_HAS_USER);
        }

        // 子角色判断
        List<EntRoleInfo> children = roleInfoService.list(new LambdaQueryWrapper<EntRoleInfo>().in(EntRoleInfo::getParentId, Arrays.asList(roleIdArray)));
        if (CollectionUtil.isNotEmpty(children)) {
            throw new RRException(RRExceptionCodeEnum.ROLE_HAS_CHILDREN);
        }

        // 判断删除的角色中是否是系统默认角色
        List<EntRoleInfo> roleInfoList = roleInfoService.list(new LambdaQueryWrapper<EntRoleInfo>().in(EntRoleInfo::getId, Arrays.asList(roleIdArray)));
        for (EntRoleInfo roleInfo : roleInfoList) {
            if (CommonConst.FlagEnum.IS.getId().equals(roleInfo.getSysFlag())) {
                throw new RRException(RRExceptionCodeEnum.ROLE_IS_SYSTEM);
            }
        }

        // 删除相关部门
//        roleDeptAssoService.remove(new LambdaQueryWrapper<RoleDeptDataAsso>().eq(RoleDeptDataAsso::getRoleId, roleIdArray));
        roleDeptAssoService.remove(new LambdaQueryWrapper<RoleDeptDataAsso>().in(RoleDeptDataAsso::getRoleId, Arrays.asList(roleIdArray)));

        // 删除菜单权限
        roleMenuInfoService.deleteBatchByRoleIdList(roleIdArray);

        return roleInfoService.removeByIds(Arrays.asList(roleIdArray));
    }

    @Override
    @Transactional
    public boolean save(EntRoleSaveReq req) {

        // 参数判断
        if (StrUtil.isBlank(req.getName()) || ObjectUtil.isNull(req.getManagerFlag())
                || !CommonConst.FlagEnum.getFlagList().contains(req.getManagerFlag())) {
            throw new RRException(RRExceptionCodeEnum.PARAM_ERROR);
        }

        LambdaQueryWrapper<EntRoleInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EntRoleInfo::getName, req.getName())
                .eq(EntRoleInfo::getTenantId, ShiroUtils.getUserTenantId());
        List<EntRoleInfo> existRoleInfoList = roleInfoService.list(queryWrapper);
        if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(existRoleInfoList)) {
            throw new RRException(RRExceptionCodeEnum.ROLE_NAME_REPEAT);
        }

        // 设置角色roleCode
        EntRoleListDo roleInfoDo = new EntRoleListDo();
        roleInfoDo.setTenantId(ShiroUtils.getUserEntity().getDeptId());
        List<EntRoleListPo> roleInfoList = roleInfoService.poList(roleInfoDo);
        String roleCode;
        if (cn.hutool.core.collection.CollectionUtil.isEmpty(roleInfoList)) {
            roleCode = "role_" + 0;
        } else {
            roleCode = "role_" + roleInfoList.get(roleInfoList.size() - 1).getId();
        }

        // 保存角色信息
        EntRoleInfo entRoleInfoDo = new EntRoleInfo();
        BeanUtil.copyProperties(req, entRoleInfoDo);
        entRoleInfoDo.setRoleCode(roleCode);
        entRoleInfoDo.setTypeId(RoleConst.RoleType.ENT.getId())
                .setTenantId(ShiroUtils.getUserEntity().getDeptId());
        if (ObjectUtil.isNull(req.getParentId())) {
            entRoleInfoDo.setParentId(RoleConst.ROLE_ROOT_PARENT_ID);
        }
        boolean saveResult = roleInfoService.save(entRoleInfoDo);
        if (!saveResult) {
            return false;
        }
        //保存角色与菜单关系
        roleMenuInfoService.saveOrUpdate(entRoleInfoDo.getId(), req.getMenuIdList());

        // 保存角色与部门id关系
        roleDeptAssoService.updateRoleDeptList(entRoleInfoDo.getId(), req.getDeptIdList());

        return true;
    }

}
